package org.Yiran.timetale_re.util.physics;

import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.joml.Vector3f;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 物理计算工具类
 * <p>
 * 用于处理力、速度、加速度的合成与计算，提供完整的物理模拟功能集合，
 * 包括牛顿力学计算、动能动量计算、风阻计算等核心物理功能
 * </p>
 *
 * @author Yiran
 * @version 1.0
 */
public class PhysicsUtils {

    // ================================
    // 物理属性数据类
    // ================================

    /**
     * 物理属性数据类
     * <p>
     * 用于封装实体的物理属性，包括力、速度、加速度和质量等核心物理量
     * </p>
     *
     * @param force        力 (N)
     * @param velocity     速度 (m/s)
     * @param acceleration 加速度 (m/s²)
     * @param mass         质量 (kg)
     */
    public record PhysicsProperty(Vec3 force, Vec3 velocity, Vec3 acceleration, double mass) {

        /**
         * 创建只有力的属性
         * <p>
         * 当只需要表示力的物理属性时使用，其他属性初始化为零向量或零值
         * </p>
         *
         * @param force 力向量
         * @param mass  质量
         * @return 包含指定力和质量的物理属性对象
         */
        public static PhysicsProperty ofForce(Vec3 force, double mass) {
            return new PhysicsProperty(force, Vec3.ZERO, Vec3.ZERO, mass);
        }

        /**
         * 创建只有速度的属性
         * <p>
         * 当只需要表示速度的物理属性时使用，其他属性初始化为零向量或零值
         * </p>
         *
         * @param velocity 速度向量
         * @param mass     质量
         * @return 包含指定速度和质量的物理属性对象
         */
        public static PhysicsProperty ofVelocity(Vec3 velocity, double mass) {
            return new PhysicsProperty(Vec3.ZERO, velocity, Vec3.ZERO, mass);
        }

        /**
         * 创建只有加速度的属性
         * <p>
         * 当只需要表示加速度的物理属性时使用，其他属性初始化为零向量或零值
         * </p>
         *
         * @param acceleration 加速度向量
         * @param mass         质量
         * @return 包含指定加速度和质量的物理属性对象
         */
        public static PhysicsProperty ofAcceleration(Vec3 acceleration, double mass) {
            return new PhysicsProperty(Vec3.ZERO, Vec3.ZERO, acceleration, mass);
        }
    }

    // ================================
    // 向量合成计算方法
    // ================================

    /**
     * 计算合力
     * <p>
     * 对给定的力向量列表进行向量加法运算，得到合力向量
     * </p>
     *
     * @param forces 所有力的列表，不能为空
     * @return 合力向量，当列表为空时返回零向量
     */
    public static Vec3 calculateResultantForce(List<Vec3> forces) {
        Vec3 resultant = Vec3.ZERO;
        for (Vec3 force : forces) {
            resultant = resultant.add(force);
        }
        return resultant;
    }

    /**
     * 计算合力（可变参数版本）
     * <p>
     * 对给定的力向量数组进行向量加法运算，得到合力向量
     * </p>
     *
     * @param forces 可变参数形式的力向量数组
     * @return 合力向量
     */
    public static Vec3 calculateResultantForce(Vec3... forces) {
        List<Vec3> forceList = new ArrayList<>(Arrays.asList(forces));
        return calculateResultantForce(forceList);
    }

    /**
     * 计算合速度
     * <p>
     * 对给定的速度向量列表进行向量加法运算，得到合速度向量
     * </p>
     *
     * @param velocities 所有速度的列表，不能为空
     * @return 合速度向量，当列表为空时返回零向量
     */
    public static Vec3 calculateResultantVelocity(List<Vec3> velocities) {
        return calculateResultantForce(velocities);
    }

    /**
     * 计算合加速度
     * <p>
     * 对给定的加速度向量列表进行向量加法运算，得到合加速度向量
     * </p>
     *
     * @param accelerations 所有加速度的列表，不能为空
     * @return 合加速度向量，当列表为空时返回零向量
     */
    public static Vec3 calculateResultantAcceleration(List<Vec3> accelerations) {
        return calculateResultantForce(accelerations);
    }

    // ================================
    // 牛顿力学计算方法
    // ================================

    /**
     * 根据牛顿第二定律计算加速度 (F = ma => a = F/m)
     * <p>
     * 使用牛顿第二定律计算物体在给定力作用下的加速度，当质量小于等于0时返回零向量
     * </p>
     *
     * @param force 力向量，单位为牛顿(N)
     * @param mass  质量，单位为千克(kg)，必须大于0
     * @return 加速度向量，单位为米每秒平方(m/s²)，当质量无效时返回零向量
     */
    public static Vec3 calculateAccelerationFromForce(Vec3 force, double mass) {
        if (mass <= 0) {
            return Vec3.ZERO;
        }
        return force.scale(1.0 / mass);
    }

    /**
     * 计算速度变化 (Δv = a * Δt)
     * <p>
     * 根据加速度和时间变化量计算速度的变化量
     * </p>
     *
     * @param acceleration 加速度向量，单位为米每秒平方(m/s²)
     * @param deltaTime    时间变化量，单位为秒(s)
     * @return 速度变化量向量，单位为米每秒(m/s)
     */
    public static Vec3 calculateVelocityChange(Vec3 acceleration, double deltaTime) {
        return acceleration.scale(deltaTime);
    }

    /**
     * 计算位置变化 (Δs = v₀ * t + 0.5 * a * t²)
     * <p>
     * 根据初始速度、加速度和时间变化量计算位置的变化量，使用匀加速直线运动公式
     * </p>
     *
     * @param initialVelocity 初始速度向量，单位为米每秒(m/s)
     * @param acceleration    加速度向量，单位为米每秒平方(m/s²)
     * @param deltaTime       时间变化量，单位为秒(s)
     * @return 位置变化量向量，单位为米(m)
     */
    public static Vec3 calculatePositionChange(Vec3 initialVelocity, Vec3 acceleration, double deltaTime) {
        Vec3 velocityComponent = initialVelocity.scale(deltaTime);
        Vec3 accelerationComponent = acceleration.scale(0.5 * deltaTime * deltaTime);
        return velocityComponent.add(accelerationComponent);
    }

    // ================================
    // 能量和动量计算方法
    // ================================

    /**
     * 计算动能 (KE = 0.5 * m * v²)
     * <p>
     * 根据质量和速度计算物体的动能
     * </p>
     *
     * @param mass     质量，单位为千克(kg)
     * @param velocity 速度向量，单位为米每秒(m/s)
     * @return 动能，单位为焦耳(J)
     */
    public static double calculateKineticEnergy(double mass, Vec3 velocity) {
        double speedSquared = velocity.lengthSqr();
        return 0.5 * mass * speedSquared;
    }

    /**
     * 计算动量 (p = m * v)
     * <p>
     * 根据质量和速度计算物体的动量向量
     * </p>
     *
     * @param mass     质量，单位为千克(kg)
     * @param velocity 速度向量，单位为米每秒(m/s)
     * @return 动量向量，单位为千克米每秒(kg·m/s)
     */
    public static Vec3 calculateMomentum(double mass, Vec3 velocity) {
        return velocity.scale(mass);
    }

    // ================================
    // 风阻计算相关方法
    // ================================

    /**
     * 计算风阻系数
     * <p>
     * 根据实体类型和横截面积计算风阻系数，不同类型实体具有不同的基础风阻系数
     * </p>
     *
     * @param entity             实体对象
     * @param crossSectionalArea 横截面积，单位为平方米(m²)
     * @return 风阻系数
     */
    public static double calculateDragCoefficient(Entity entity, double crossSectionalArea) {
        // 基础风阻系数
        double baseDrag = 0.47; // 球体的阻力系数

        // 根据实体类型调整
        if (entity instanceof net.minecraft.world.entity.player.Player) {
            baseDrag = 0.7; // 玩家站立时的风阻
        } else if (entity instanceof net.minecraft.world.entity.item.ItemEntity) {
            baseDrag = 0.4; // 掉落物较小的风阻
        } else if (entity instanceof net.minecraft.world.entity.LivingEntity) {
            baseDrag = 0.8; // 生物的风阻
        }

        return baseDrag * crossSectionalArea;
    }

    /**
     * 计算风力对实体的影响（考虑风阻）
     * <p>
     * 根据风向量和空气密度计算作用在实体上的风力，考虑了相对速度和风阻效应
     * </p>
     *
     * @param entity      实体对象
     * @param windVector  风力向量，使用JOML库的Vector3f类型
     * @param airDensity  空气密度，单位为千克每立方米(kg/m³)
     * @return 作用在实体上的风力向量，单位为牛顿(N)
     */
    public static Vec3 calculateWindForce(Entity entity, Vector3f windVector, double airDensity) {
        // 计算实体相对风的速度
        Vec3 entityVelocity = entity.getDeltaMovement();
        Vec3 windVelocity = new Vec3(windVector.x(), windVector.y(), windVector.z());
        Vec3 relativeVelocity = windVelocity.subtract(entityVelocity);

        // 计算相对速度大小
        double relativeSpeed = relativeVelocity.length();

        if (relativeSpeed < 0.01) {
            return Vec3.ZERO;
        }

        // 计算横截面积（简化计算）
        double crossSectionalArea = calculateCrossSectionalArea(entity);

        // 计算风阻系数
        double dragCoefficient = calculateDragCoefficient(entity, crossSectionalArea);

        // 使用风阻公式: F = 0.5 * ρ * v² * Cd * A
        double windForceMagnitude = 0.5 * airDensity * relativeSpeed * relativeSpeed * dragCoefficient * crossSectionalArea;

        // 力的方向与相对风速方向一致
        Vec3 forceDirection = relativeVelocity.normalize();

        return forceDirection.scale(windForceMagnitude);
    }

    /**
     * 计算实体的横截面积（近似值）
     * <p>
     * 通过实体的边界框尺寸近似计算其横截面积，假设实体为圆柱体形状
     * </p>
     *
     * @param entity 实体对象
     * @return 横截面积，单位为平方米(m²)
     */
    private static double calculateCrossSectionalArea(Entity entity) {
        double width = entity.getBbWidth();
        double height = entity.getBbHeight();

        // 简化为圆柱体的正面投影面积
        return Math.PI * (width / 2) * (width / 2);
    }

    /**
     * 计算玩家移动方向与风向的交互力
     * <p>
     * 根据玩家移动方向和风力计算交互后的合力，考虑顺风、逆风和侧风等不同情况
     * </p>
     *
     * @param playerMoveDirection 玩家移动方向向量
     * @param windForce           风力向量
     * @param playerSpeed         玩家移动速度
     * @param playerMass          玩家质量
     * @return 交互后的合力向量
     */
    public static Vec3 calculatePlayerWindInteraction(Vec3 playerMoveDirection, Vec3 windForce,
                                                      double playerSpeed, double playerMass) {
        Vec3 windDirection = windForce.normalize();
        double windMagnitude = windForce.length();

        // 计算玩家移动方向与风向的夹角余弦值
        double angleCos = playerMoveDirection.normalize().dot(windDirection);

        // 根据夹角调整风力影响
        double effectiveWindForce;
        if (angleCos > 0.7) {
            // 顺风：提供助力 (减少阻力)
            effectiveWindForce = windMagnitude * (1.0 - angleCos * 0.3);
        } else if (angleCos < -0.7) {
            // 逆风：增加阻力
            effectiveWindForce = windMagnitude * (1.0 - angleCos * 0.7);
        } else {
            // 侧风：产生侧向力和轻微阻力
            effectiveWindForce = windMagnitude * (1.0 + Math.abs(angleCos) * 0.2);
        }

        // 计算玩家主动施加的力（基于速度和质量）
        double playerForce = playerSpeed * playerMass * 0.1;

        // 合成最终力
        Vec3 resultantDirection;
        double resultantMagnitude;

        if (Math.abs(angleCos) > 0.9) {
            // 基本同向或反向
            resultantDirection = playerMoveDirection;
            resultantMagnitude = playerForce + effectiveWindForce * angleCos;
        } else {
            // 有角度差异，需要向量合成
            Vec3 playerForceVec = playerMoveDirection.scale(playerForce);
            Vec3 windForceVec = windDirection.scale(effectiveWindForce);
            Vec3 resultant = playerForceVec.add(windForceVec);
            resultantDirection = resultant.normalize();
            resultantMagnitude = resultant.length();
        }

        return resultantDirection.scale(resultantMagnitude);
    }

    // ================================
    // 实体物理状态应用方法
    // ================================

    /**
     * 应用力到实体（更新实体运动状态）
     * <p>
     * 将给定的力应用到实体上，更新实体的速度状态，基于F=ma公式计算加速度
     * </p>
     *
     * @param entity 目标实体
     * @param forces 要应用的力，可变参数形式
     */
    public static void applyForcesToEntity(Entity entity, Vec3... forces) {

        // F = ma => a = F/m
        // 简化处理，假设质量为1或已包含在力的计算中
        Vec3 acceleration = calculateResultantForce(forces);

        // 更新实体速度 (v = v0 + a*t, 这里简化t=1)
        Vec3 currentVelocity = entity.getDeltaMovement();
        Vec3 newVelocity = currentVelocity.add(acceleration);

        entity.setDeltaMovement(newVelocity);
    }

    /**
     * 应加速度到实体
     * <p>
     * 将给定的加速度应用到实体上，更新实体的速度状态
     * </p>
     *
     * @param entity         目标实体
     * @param accelerations 要应用的加速度，可变参数形式
     */
    public static void applyAccelerationsToEntity(Entity entity, Vec3... accelerations) {
        Vec3 resultantAcceleration = calculateResultantAcceleration(List.of(accelerations));

        // 更新实体速度
        Vec3 currentVelocity = entity.getDeltaMovement();
        Vec3 newVelocity = currentVelocity.add(resultantAcceleration);

        entity.setDeltaMovement(newVelocity);
    }

    /**
     * 应速度到实体
     * <p>
     * 将给定的速度直接应用到实体上，替换实体的当前速度
     * </p>
     *
     * @param entity     目标实体
     * @param velocities 要应用的速度，可变参数形式
     */
    public static void applyVelocitiesToEntity(Entity entity, Vec3... velocities) {
        Vec3 resultantVelocity = calculateResultantVelocity(List.of(velocities));

        entity.setDeltaMovement(resultantVelocity);
    }

    /**
     * 计算考虑物理合成的新位置
     * <p>
     * 根据实体当前状态和给定的加速度计算在时间增量后的新位置
     * </p>
     *
     * @param entity      实体对象
     * @param deltaTime   时间增量，单位为秒(s)
     * @param accelerations 作用的加速度，可变参数形式
     * @return 新位置向量
     */
    public static Vec3 calculateNewPosition(Entity entity, double deltaTime, Vec3... accelerations) {
        Vec3 resultantAcceleration = calculateResultantAcceleration(List.of(accelerations));
        Vec3 currentVelocity = entity.getDeltaMovement();

        // 计算位移 s = v₀*t + 0.5*a*t²
        Vec3 displacement = calculatePositionChange(currentVelocity, resultantAcceleration, deltaTime);

        return entity.position().add(displacement);
    }

    // ================================
    // 物理属性合成方法
    // ================================

    /**
     * 合成多个物理属性
     * <p>
     * 将多个物理属性对象进行合成，得到总的物理属性
     * </p>
     *
     * @param properties 物理属性列表，不能为空
     * @return 合成后的物理属性对象
     */
    public static PhysicsProperty combinePhysicsProperties(List<PhysicsProperty> properties) {
        Vec3 totalForce = Vec3.ZERO;
        Vec3 totalVelocity = Vec3.ZERO;
        Vec3 totalAcceleration = Vec3.ZERO;
        double totalMass = 0;

        for (PhysicsProperty property : properties) {
            totalForce = totalForce.add(property.force());
            totalVelocity = totalVelocity.add(property.velocity());
            totalAcceleration = totalAcceleration.add(property.acceleration());
            totalMass += property.mass();
        }

        return new PhysicsProperty(totalForce, totalVelocity, totalAcceleration, totalMass);
    }
}